Repository navigation
Conversation
Every point_template ForceSpawn loads a new spawn group and merges it into the owner spawn group (the map's group by default). The merge moves the child's resource manifests into the owner, which keeps them until map change, so each ForceSpawn adds one manifest and later ForceSpawns get slower (0.53 ms -> 1.47 ms per call after 65k calls, nothing left alive). With the new experimental cvar enabled, the manifests a merge hands over are destroyed right away, the same way the spawn group destructor does it. ForceSpawn cost then stays flat (0.49 ms per call after 141k calls). The hook fails soft: a missing signature or offset only disables the cvar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Name it after the symptom server operators see, matching cs2f_fix_game_bans. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Every
point_templateForceSpawngets slower than the one before it, and the cost only resets on map change. This happens even if everything spawned is removed right away. On maps that spawn a lot, such as ZE maps with procedural or repeated spawns (for exampleze_diddle extremeandze_collective), this adds up to a noticeable server hitch within one map.This PR adds an opt-in cvar,
cs2f_fix_forcespawn_slowdown(default0). With it on, ForceSpawn cost stays flat for the whole map.Cause
From reverse engineering engine2 and server on the 1.41.9.1 Windows and Linux binaries:
ForceSpawnloads a new spawn group. When the spawn finishes,CBaseSpawnGroupmerges that group into its owner spawn group. For the defaultINSERT_INTO_POINT_TEMPLATE_SPAWN_GROUPthe owner is the map's spawn group. The merge is the vfunc that logs"%s: Transfering creation sequence %d to owner spawn group %s".IGameResourceManifestlist (theCUtlVectorat+0x228) to the owner's list.IGameResourceService(+0x220), vfunc 31, on each one. So the map spawn group gains one manifest per ForceSpawn, and per-spawn engine work grows with that list.I confirmed this by checking the owner's list during a stress run. With the fix off it grows by exactly one per ForceSpawn (111k+ entries after one 10-minute run). With the fix on it stays at the map's own count.
Fix
src/spawngroupfix.cpphooksCBaseSpawnGroup_MergeIntoOwner:IGameResourceServicecall the spawn group destructor uses.Details:
CBaseSpawnGroup_MergeIntoOwner(Windows and Linux), and three offsets:CBaseSpawnGroup_ResourceManifests(552),CBaseSpawnGroup_ResourceService(544) andIGameResourceService_DestroyManifest(31). All four have the same values on Windows and Linux.cs2fixes.cfg. That file is re-executed on every map load, so a value set from the console is overwritten on the next changelevel.Results
Test setup: a public repro map, ForceSpawn Stress Test (workshop 3823325930). Chat
!stressspawns a template and removes it right away, for 6 ms of every Think, and reports ms per call every 5 s.Linux dedicated server, 10 min each, external measurement with
pidstat"CPU ms per ForceSpawn" is process CPU time, from pidstat at 10 s intervals, divided by the ForceSpawn calls in the same interval.
cs2f_fix_forcespawn_slowdown 0cs2f_fix_forcespawn_slowdown 1AI disclosure
I used Claude Code for the reverse engineering, the code, and polishing this PR description.